package com.hitqz.robot.dispatch.netty.listener;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.fastjson.JSONObject;
import com.google.common.cache.Cache;
import com.google.common.util.concurrent.RateLimiter;
import com.hitqz.robot.api.business.dto.CmdRobot2PosDto;
import com.hitqz.robot.api.business.dto.RobotJobDto;
import com.hitqz.robot.api.business.dto.RobotSendClientMsgDto;
import com.hitqz.robot.api.business.entity.RobotEntity;
import com.hitqz.robot.api.business.entity.RobotJobEntity;
import com.hitqz.robot.api.dispatch.model.MessageDto;
import com.hitqz.robot.api.dispatch.protocol.ProtocolEnum;
import com.hitqz.robot.api.dispatch.protocol.dto.*;
import com.hitqz.robot.biz.config.CustomConstant;
import com.hitqz.robot.biz.service.RobotJobService;
import com.hitqz.robot.biz.service.RobotService;
import com.hitqz.robot.common.core.constant.CommonConstants;
import com.hitqz.robot.common.core.util.R;
import com.hitqz.robot.dispatch.netty.ChannelCache;
import com.hitqz.robot.dispatch.netty.service.DispatchService;
import com.hitqz.robot.dispatch.netty.util.LockUtil;
import com.hitqz.robot.dispatch.netty.util.RobotReplyUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;
import java.util.LinkedList;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author xupkun
 * @date 2024/7/10
 */
@Component
@Slf4j
@AllArgsConstructor
public class Send2ClientListener {

	private final RobotJobService robotJobService;

	private final DispatchService dispatchService;

	private final RobotService robotService;

	/**
	 * 发送消息给客户端
	 * @param dto
	 */
	@EventListener
	@Async("serverSendThreadPool")
	public void handlerSend(RobotSendClientMsgDto dto) {
		MessageDto messageDto = new MessageDto();
		messageDto.setClientId(dto.getClientId());
		messageDto.setCmd(dto.getCmd());
		messageDto.setSn(dto.getSeq() != null ? dto.getSeq() : 0);
		if (dto.getData() != null) {
			String data = JSONObject.toJSONString(dto.getData());
			messageDto.setData(data.getBytes(StandardCharsets.UTF_8));
		}
		ChannelCache.send2Client(dto.getClientId(), messageDto);
	}


	@EventListener
	@Async("serverSendThreadPool")
	public void handlerSend(CmdRobot2PosDto dto) {
		MessageDto messageDto= new MessageDto();
		messageDto.setSn(0);
		messageDto.setCmd(ProtocolEnum.ROBOT_NAV_POS_CMD.getCmd());
		CmdRobot2PosDto posDto = new CmdRobot2PosDto();
		posDto.setPosCode(dto.getPosCode());
		messageDto.setData(JSONObject.toJSONString(posDto).getBytes(StandardCharsets.UTF_8));
		messageDto.setClientId(dto.getClientId());
		ChannelCache.send2Client(dto.getClientId(),messageDto);
	}

	/**
	 * 修改机器人信息
	 * @param robotEntity
	 */
	@EventListener
	@Async("serverSendThreadPool")
	public void handlerRobotModel(RobotEntity robotEntity) {
		MessageDto messageDto= new MessageDto();
		messageDto.setSn(0);
		messageDto.setCmd(ProtocolEnum.ROBOT_INFO.getCmd());
		RobotDto robotDto = new RobotDto();
		BeanUtil.copyProperties(robotEntity, robotDto, false);
		messageDto.setData(JSONObject.toJSONString(robotDto).getBytes(StandardCharsets.UTF_8));
		messageDto.setClientId(robotEntity.getRobotSn());
		ChannelCache.send2Client(robotDto.getRobotSn(),messageDto);
	}
	@EventListener
	@Async("serverSendThreadPool")
	public void listenerLog(AskLogMessageDto reportLogMessageDto) {
		MessageDto messageDto= new MessageDto();
		messageDto.setSn(0);
		messageDto.setCmd(ProtocolEnum.REPORT_LOG_MESSAGE.getCmd());
		messageDto.setData(JSONObject.toJSONString(reportLogMessageDto).getBytes(StandardCharsets.UTF_8));
		messageDto.setClientId(reportLogMessageDto.getClientId());
		ChannelCache.send2Client(reportLogMessageDto.getClientId(),messageDto);
	}

	@EventListener
	public void handlerDoNext(RobotNextStepDto robotNextStepDto) {
		MessageDto messageDto= new MessageDto();
		messageDto.setSn(0);
		messageDto.setClientId(robotNextStepDto.getClientId());
		messageDto.setCmd(ProtocolEnum.CMD_NEXT_STEP.getCmd());
		messageDto.setData(JSONObject.toJSONString(robotNextStepDto).getBytes(StandardCharsets.UTF_8));
		ChannelCache.send2Client(robotNextStepDto.getClientId(),messageDto);
	}




	@EventListener
	@Async("serverSendThreadPool")
	public void handler(RobotJobDto robotJobDto) {
		RobotJobEntity robotJobEntity = robotJobService.getById(robotJobDto.getId());
		// 1 调度组id 后续修改
		String robotSn = dispatchService.findExecuteRobot(robotJobEntity.getGroupId());
		if (StrUtil.isEmpty(robotSn)) {
			log.info("后续增加缓存任务");
			return;
		}
        RateLimiter rateLimiter = CustomConstant.ticketsControlLimiterMap.computeIfAbsent(robotSn, k -> RateLimiter.create(1));
        if (!rateLimiter.tryAcquire(10)){
            log.info("10秒内多次下发,不执行");
            return;
        }
		RobotEntity robotEntity = robotService.selectByRobotSn(robotSn);

		if (robotEntity == null) {
			// 缓存
			Cache<String, LinkedList<RobotJobDto>> posInfoDtoCache = SpringUtil.getBean("taskList");
			LinkedList<RobotJobDto> robotJobEntities = posInfoDtoCache.getIfPresent("task");
			robotJobEntities.add(robotJobDto);
			posInfoDtoCache.put("task", robotJobEntities);
		} else {
			Cache<Integer, RobotEntity> executeRobot = SpringUtil.getBean("executeTaskRobot");
			executeRobot.put(robotJobEntity.getId(), robotEntity);

			MessageDto messageDto= new MessageDto();
			messageDto.setSn(0);
			messageDto.setCmd(ProtocolEnum.ROBOT_CMD_TICKETS.getCmd());
			RobotCmdTicketsDto robotCmdTicketsDto = new RobotCmdTicketsDto();
			robotCmdTicketsDto.setCmd(0);//启动
			robotCmdTicketsDto.setTaskId(robotJobDto.getId());
			//robotJobEntity.getContent()
			if (StrUtil.isNotBlank(robotJobEntity.getContent())){
				FormConfigModalDto formConfigModalDto = JSONObject.parseObject(robotJobEntity.getContent(), FormConfigModalDto.class);
				for (FormFieldConfigModalDto field : formConfigModalDto.getFields()) {
					field.setOptions(null);
				}
				robotCmdTicketsDto.setTaskContent(JSONObject.toJSONString(formConfigModalDto));
			}
			robotCmdTicketsDto.setTaskName(robotJobEntity.getName());
			robotCmdTicketsDto.setClientId(robotEntity.getRobotSn());
			messageDto.setClientId(robotEntity.getRobotSn());
			messageDto.setData(JSONObject.toJSONString(robotCmdTicketsDto).getBytes(StandardCharsets.UTF_8));
			Lock lock = LockUtil.ROBOT_CMD_TICKETS_LOCKS.get(robotEntity.getRobotSn(),ProtocolEnum.ROBOT_CMD_TICKETS.getCmd() );
			if (lock == null) {
				lock = new ReentrantLock();
				LockUtil.ROBOT_CMD_TICKETS_LOCKS.put(robotEntity.getRobotSn(), ProtocolEnum.ROBOT_CMD_TICKETS.getCmd(), lock);
			}
			synchronized (lock) {
				try {
					ChannelCache.send2Client(messageDto.getClientId() ,messageDto);
					lock.wait(2000);
					R result = RobotReplyUtil.COMMON_REPLY_TABLE.get(robotEntity.getRobotSn(), ProtocolEnum.ROBOT_CMD_TICKETS.getCmd());
					log.info("result:{}",result);
					if (result.getCode() == CommonConstants.FAIL) {
						//throw  new IllegalArgumentException(result.getMsg());
					}
				} catch (Exception e) {

				}
			}

		}
	}

	@EventListener
	@Async("serverSendThreadPool")
	public void handler(ModifySysInfoDto modifySysInfoDto) {
		MessageDto messageDto= new MessageDto();
		messageDto.setSn(ProtocolEnum.MODIFY_SYS_INF.getCmd());
		messageDto.setCmd(ProtocolEnum.MODIFY_SYS_INF.getCmd());
		messageDto.setData(JSONObject.toJSONString(modifySysInfoDto).getBytes(StandardCharsets.UTF_8));
		ChannelCache.send2All(messageDto);
	}

	@EventListener
	@Async("serverSendThreadPool")
	public void handlerNav(RobotCmdNavDto robotCmdNavDto) {
		log.info("下发导航状态:" + JSONObject.toJSONString(robotCmdNavDto));
		MessageDto messageDto= new MessageDto();
		messageDto.setSn(ProtocolEnum.ROBOT_NAV_CMD.getCmd());
		messageDto.setCmd(ProtocolEnum.ROBOT_NAV_CMD.getCmd());
		messageDto.setData(JSONObject.toJSONString(robotCmdNavDto).getBytes(StandardCharsets.UTF_8));
		messageDto.setClientId(robotCmdNavDto.getClientId());
		ChannelCache.send2Client(robotCmdNavDto.getClientId(), messageDto);
	}




}
